/*
 *    Copyright 2014 Sam Y Ge
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.strx;

import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 * XML解析器。
 * </p>
 * 
 * <p>
 * 通过给定的数据来源，解析出期待的数据，并根据给定的算法封装出对象<br/>
 * 调用者必须自己定义封装过程
 * </p>
 * 
 * @see Status
 * @see TagHandler
 * @see TagType
 * 
 * @author <a href="mailto:samuel.y@foxmail.com">Sam Y Ge</a>
 */
public final class Parser {
	/* 标签堆栈 */
	private TagStack tagStack = new TagStack();

	/* Tag读取器 */
	private TagReader tagReader;

	/* Tag处理器队列 */
	private final List<TagHandler<?>> tagHandleQueue = new ArrayList<TagHandler<?>>();

	/**
	 * 通过给定XML字符串构造解析器。
	 * 
	 * @param xml
	 *            XML字符串
	 * @param tagHandle
	 *            Tag处理器
	 */
	public Parser(String xml, TagHandler<?> tagHandle) {
		tagReader = new StringTagReader(xml);
		if (null != tagHandle) {
			tagHandleQueue.add(tagHandle);
		}
	}

	/**
	 * 通过给定输入流构造解析器。
	 * 
	 * @param reader
	 *            输入流
	 * @param tagHandle
	 *            Tag处理器
	 */
	public Parser(Reader reader, TagHandler<?> tagHandle) {
		tagReader = new StreamTagReader(reader);
		if (null != tagHandle) {
			tagHandleQueue.add(tagHandle);
		}
	}

	/**
	 * <p>
	 * 解析方法。
	 * </p>
	 * 
	 * <p>
	 * 构造解析器之后必须调用该方法，之后方可从TagHandle中取结果
	 * </p>
	 * 
	 * @see TagHandler
	 */
	public void parse() {
		while (true) {
			// 如果TagReader被清理了，则结束解析
			if (null == tagReader) {
				break;
			}

			Tag tag = tagReader.getNextTag();

			// 无标签或者解析出错时终止
			if (null == tag) {
				break;
			}

			Status status = null;
			Tag top = null;

			boolean needContinue = true;

			// 将当前解析状态传递给TagHandle处理
			switch (tag.getType()) {
			case START:
				tagStack.push(tag);
				top = tag;
				status = new Status(tagStack, top);
				needContinue = fireTagEvent(status, TagType.START);
				break;
			case END:
				if (!tagStack.empty()) {
					top = tagStack.peek();
					status = new Status(tagStack, top);
					tagStack.pop();
					needContinue = fireTagEvent(status, TagType.END);
				}
				break;
			case SELF_CLOSE:
				tagStack.push(tag);
				top = tag;
				status = new Status(tagStack, top);
				needContinue = fireTagEvent(status, TagType.START);
				if (needContinue) {
					needContinue = fireTagEvent(status, TagType.END);
				}
				tagStack.pop();
				break;
			case CDATA:
				top = tag;
				status = new Status(tagStack, top);
				needContinue = fireTagEvent(status, TagType.CDATA);
				break;
			default:
			}

			if (!needContinue) {
				break;
			}
		}

		// 解析完毕后清除TagStack并将所有TagHandler的运行位设false
		tagStack.clean();
		for (TagHandler<?> handler : tagHandleQueue) {
			handler.processing = false;
		}
	}

	/**
	 * 追加TagHandle。
	 * 
	 * 追加一个TagHandle用于处理相同的报文
	 * 
	 * @param tagHandle
	 *            新追加的TagHandle
	 */
	public void pushHandle(TagHandler<?> tagHandle) {
		if (tagHandle != null) {
			tagHandleQueue.add(tagHandle);
		}
	}

	/*
	 * 触发Tag事件
	 */
	private boolean fireTagEvent(Status status, TagType type) {
		boolean needContinue = true;

		// 触发过程中，记录需要被移除的TagHandle
		List<TagHandler<?>> removeList = new LinkedList<TagHandler<?>>();
		for (int i = 0; i < tagHandleQueue.size(); i++) {
			TagHandler<?> handle = tagHandleQueue.get(i);
			needContinue = handle.notifyProcess(status, type);
			if (!needContinue) {
				break;
			}

			if (handle.isNeed2Remove()) {
				removeList.add(handle);
			}
		}

		if (needContinue) {
			// 开始移除
			for (TagHandler<?> tagHandle : removeList) {
				tagHandleQueue.remove(tagHandle);
			}

			// 清理移除列表
			removeList.clear();
		}

		return needContinue;
	}

	/**
	 * <p>
	 * 清理解析器。
	 * </p>
	 * 
	 * <p>
	 * 释放解析器所持资源, 但不关闭TagReader
	 * </p>
	 */
	public void clean() {
		// 清理Tag栈
		tagStack.clean();

		// 清理所有TagHandle后移出出队列
		for (int i = 0; i < tagHandleQueue.size(); i++) {
			TagHandler<?> handle = tagHandleQueue.get(i);
			handle.clean();
		}
		tagHandleQueue.clear();
	}

	/**
	 * <p>
	 * 关闭解析器。
	 * </p>
	 * 
	 * <p>
	 * 释放解析器所持资源
	 * </p>
	 */
	public void close() {
		clean();

		// 清理TagReader
		if (tagReader != null) {
			tagReader.clean();
			tagReader = null;
		}

		tagStack = null;
	}

}
